package com.king.juc.happens_before;

/**
 * happens-before 7个规则
 * 规定了对共享变量的写操作对其他线程的读操作可见, 他是可见性与有序性的一套规则总结,
 * 抛开以下规则, JMM并不能保证一个线程对共享变量的写, 对于其它线程对该共享变量的读可见
 * @author king
 * @date 2020/11/28 19:56
 */
public class HappensBefore {

    // 线程解锁m之前对变量的写, 对于接下来对m加锁的其他线程该变量的读可见
    static int x;
    static Object m = new Object();

    private void one(){
        new Thread(() -> {
            synchronized (m){
                x = 10;
            }
        }, "t1").start();

        new Thread(() -> {
            synchronized (m){
                System.out.println(x);
            }
        }, "t2").start();
    }


    // 线程对volatile变量的写, 对接下来其他线程对该变量的读可见
    volatile static int a;

    private void two(){
        new Thread(() -> {
            a = 10;
        }, "t1").start();

        new Thread(() -> {
            System.out.println(a);
        }, "t2").start();
    }

    // 线程start前对变量的写, 对该线程开始后对该变量的读可见
    static int b;

    private void three(){
        b = 10;

        new Thread(() -> {
            System.out.println(b);
        }, "t1").start();
    }

    // 线程结束前对变量的写, 对其他线程得知它结束后的读可见(比如其他线程调用t1.isAlive()或t1.join()等待它结束)
    static int d;

    private void four(){
        Thread t1 = new Thread(() -> {
            d = 10;
        }, "t1");
        t1.start();

        try {
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(d);
    }

    // 线程 t1 打断 t2(interrupt)前对变量的写, 对于其他线程得知 t2 被打断后对变量的读可见(通过 t2.interrupted 或 t2.isInterrupted)
    static int e;

    private void five(){
        Thread t2 = new Thread(() -> {
            while(true){
                if(Thread.currentThread().isInterrupted()){
                    System.out.println(e);
                    break;
                }
            }
        }, "t2");
        t2.start();

        new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            e = 10;
            t2.interrupt();
        }, "t1").start();

        while(!t2.isInterrupted()){
            Thread.yield();
        }
        System.out.println(e);
    }

    // 对变量默认值(0, flase, null)的写, 对其它线程对该变量的读可见

    // 具有传递性, 如果 x hb -> y 并且 y hb -> z 那么有 x hb -> z, 配合volatile的防指令重排, 有下面的例子
    volatile static int f;
    static int y;

    private void six(){
        new Thread(() -> {
            y = 10;
            f = 20;
        }, "t1").start();

        new Thread(() -> {
            // x = 20 对 t2 可见, 同时 y = 10 也对 t2 可见
            System.out.println(f);
        }, "t2").start();
    }

}
